package cn.cuilan.common.server;

import cn.cuilan.common.core.AbstractProtocolConnector;
import cn.cuilan.common.entity.ProtocolMessage;
import cn.cuilan.common.pipeline.ProtocolChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * 私有协议服务端
 *
 * @author zhang.yan
 */
public class ProtocolMessageServer<T extends ProtocolMessage>
        extends AbstractProtocolConnector<ServerConfiguration, ServerBootstrap, T> {

    public ProtocolMessageServer(int port) {
        this(port, ServerConfiguration.newBuilder().build());
    }

    public ProtocolMessageServer(int port, ServerConfiguration configuration) {
        super(configuration);
        setSocketAddress(new InetSocketAddress(port));
    }

    public void start() throws InterruptedException {
        getBootstrap().bind().addListener((ChannelFuture future) -> {
            final Channel channel = future.channel();
            setChannel(channel);
            logger.info("Server is started and listening at {}", channel.localAddress());
        }).sync().await();
    }

    @Override
    protected ServerBootstrap createBootstrap() {
        final ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(getBossEventLoopGroup(), getWorkerEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .localAddress(getSocketAddress())
                .childHandler(new ProtocolChannelInitializer<>(
                        getConfiguration(),
                        getConfigurer(),
                        getWorkerEventLoopGroup(),
                        getMessageHandler()));
        configureBootstrap(bootstrap);
        bootstrap.validate();
        return bootstrap;
    }

    @Override
    public void shutdown() {
        stop();
        super.shutdown();
    }

    /**
     * True if server is ready to accept connections.
     */
    public boolean isStarted() {
        final Channel channel = getChannel();
        return channel != null && channel.isOpen();
    }

    public void stop() {
        final Channel channel = getChannel();
        if (channel == null) {
            throw new IllegalStateException("Server is not started.");
        }
        logger.info("Stopping the Server...");
        try {
            channel.closeFuture();
            channel.disconnect();
            channel.close().sync().await(10, TimeUnit.SECONDS);
            logger.info("Server was Stopped.");
        } catch (InterruptedException e) {
            logger.error("Error while stopping the server", e);
        }
    }
}
